using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;


namespace songofbirds
{
    struct Particle2D
    {
        // Starting position of that particle (t = 0)
        public Vector2 startPosition;
        // Center point to rotate/draw
        public Vector2 origin;
        // Movement direction of the particle
        public Vector2 direction;
        // Speed of the particle in units/second
        public float speed;
        // Angle of the particle
        public float startAngle;
        // Rotation of the particle in rads/second
        public float rotation;
        // The time since the particle system was created that this
        // particle came into use
        public float startTime;

        // Source rectange for texturing
        public Rectangle? rect;
        
        public Particle2D(Vector2 StartPosition, Vector2 Origin, Rectangle Rect,
        Vector2 Direction, float Speed, float StartAngle, float Rotation,
            float StartTime)
        {
            startPosition = StartPosition;
            origin = Origin;
            rect = Rect;
            direction = Direction;
            speed = Speed;
            startAngle = StartAngle;
            rotation = Rotation;
            startTime = StartTime;
        }
    }

    public class Particle2DSystem
    {
        // Particle settings
        int _nParticles;
        Vector2 _particleSize;
        float _lifespan = 1;
        Vector2 _wind;
        Texture2D _texture;
        float _fadeInTime;
        Rectangle? _srcRect;
        
        // Particles
        Particle2D[] _particles;

        // Queue variables
        int _activeStart = 0, _nActive = 0;
        // Time particle system was created
        DateTime _start;

        public Particle2DSystem( Texture2D tex, 
            Rectangle? srcRect, int nParticles,
            Vector2 particleSize, float lifespan,
            Vector2 wind, float FadeInTime)
        {
            _nParticles = nParticles;
            _particleSize = particleSize;
            _lifespan = lifespan;
            _wind = wind;
            _texture = tex;
            _srcRect = srcRect;
            _fadeInTime = FadeInTime;
            _start = DateTime.Now;
            generateParticles();
        }

        void generateParticles()
        {
            _particles = new Particle2D[_nParticles];
            // Initialize particle settings and fill index and vertex arrays
            for (int i = 0; i < _nParticles; i++)
                _particles[i] = new Particle2D(Vector2.Zero,  Vector2.Zero, new Rectangle(), 
                    Vector2.Zero, 0, 0, 0, 0);
        }

        // Marks another particle as active and applies the given settings to it
        public void AddParticle(Vector2 Position, Rectangle? srcRect, 
            Vector2 Direction, float Speed, float StartAngle, float Rotation)
        {
            // If there are no available particles, give up
            if (_nActive + 1 == _nParticles)
                return;

            // Determine the index at which this particle should be created
            int index = offsetIndex(_activeStart, _nActive);
            _nActive ++;
            // Determine the start time
            float StartTime = (float)(DateTime.Now - _start).TotalSeconds;
            // Set the particle settings to the particle's vertex

            _particles[index].startPosition = Position;
            _particles[index].direction = Direction;
            _particles[index].speed = Speed;
            _particles[index].startTime = StartTime;
            _particles[index].rect = srcRect;
            _particles[index].startAngle = StartAngle;
            _particles[index].rotation = Rotation;
        }

        // Increases the 'start' parameter by 'count' positions, wrapping
        // around the particle array if necessary
        int offsetIndex(int start, int count)
        {
            for (int i = 0; i < count; i++)
            {
                start++;
                if (start == _particles.Length)
                    start = 0;
            }
            return start;
        }

        public void Update()
        {
            float now = (float)(DateTime.Now - _start).TotalSeconds;
            int startIndex = _activeStart;
            int end = _nActive;
            // For each particle marked as active...
            for (int i = 0; i < end; i++)
            {
                // If this particle has gotten older than 'lifespan'...
                if (_particles[_activeStart].startTime < now - _lifespan)
                {
                    // Advance the active particle start position past
                    // the particle's index and reduce the number of
                    // active particles by 1

                    _activeStart++;
                    _nActive--;
                    if (_activeStart == _particles.Length)
                        _activeStart = 0;
                }
            }

        }

        public void Draw(SpriteBatch SpriteBatch)
        {
            float time = (float)(DateTime.Now - _start).TotalSeconds;
            Vector2 position;
            Rectangle? srcRect;
            Color color;
            Particle2D particle;
            for (int i = 0; i < _particles.Length; i++)
            {
                particle = _particles[i];
                float relativeTime = time - particle.startTime;
                // Move the particle along its movement direction and the wind direction
                if (particle.direction.X == 0 && particle.direction.Y == 0)
                    position = particle.startPosition;
                else
                    position = (particle.direction * particle.speed + _wind) * relativeTime + particle.startPosition;

                // Rotate the particle.
                float angle = particle.rotation * relativeTime + particle.startAngle;

                srcRect = particle.rect;
                if (srcRect == null) srcRect = _srcRect;

                // Fade out towards end of life
                float d = MathHelper.Clamp(1.0f - (float)Math.Pow((relativeTime / _lifespan), 10), 0, 1);
                // Fade in at beginning of life
                d *= MathHelper.Clamp((relativeTime / _fadeInTime), 0, 1);
                // Return color * fade amount
                color = Color.White * d;
                SpriteBatch.Draw(_texture, position, srcRect, color, angle, particle.origin, 1, SpriteEffects.None, 0);
                
            }
        }
        
    }

}
